#!/usr/bin/env python
# -*- coding: utf-8 -*-

#
# This program is free software; you can redistribute it and/or modify  
# it under the terms of the GNU General Public License as published by  
# the Free Software Foundation; either version 2 of the License, or     
# (at your option) any later version.                                   
#                                                                         
# A copy of the license can be found in the license.txt file supplied   
# with this software or at: http://www.gnu.org/copyleft/gpl.html       
#

import time,socket,fcntl,errno

from spinage.net  import ioloop,iostream
from spinage.help import logging

from spinage.help.codecchain import CodecChain
from spinage.help import protocol

from spinage.session import Session


class   Server(object) :

    def __init__(self) :
        self._logger = logging.getLogger(self.__class__.__name__)
        self._io_loop = ioloop.IOLoop()

        self._codec_chain = CodecChain()
        self._protocol_factory = None

        self._handler = None

        self._addresses_listening = {}
        self._sockets_listening = {}
        ## __init__()

    def setCodecChain(self, codec_chain) :
        if codec_chain :
            self._codec_chain = codec_chain
        ## setCodecChain()

    def getCodecChain(self) :
        return self._codec_chain
        ## getCodecChain()

    def setProtocolFactory(self, protocol_factory) :
        if protocol_factory :
            self._protocol_factory = protocol_factory
        ## setProtocolFactory()

    def shutdown(self) :
        if self._io_loop.running() :
            self._io_loop.stop()
        ## shutdown()

    def setHandler(self, handler) :
        '''set handler, and discard old one
        '''
        if handler :
            self._handler = handler
        ## setHandler()

    def listen(self, address) :
        if isinstance(address, int) :
            port = address
            address = ('', port)

        if address not in self._addresses_listening :
            self.__do_listen( address )
        ## listen()
        
    def __do_listen(self, address) :
        _socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        flags = fcntl.fcntl(_socket.fileno(), fcntl.F_GETFD)
        flags |= fcntl.FD_CLOEXEC
        fcntl.fcntl(_socket.fileno(), fcntl.F_SETFD, flags)
        _socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        _socket.setblocking(0)
        _socket.bind(address)
        _socket.listen(128)
        self._io_loop.add_handler(_socket.fileno(), self.__onConnectionReady, self._io_loop.READ)

        self._addresses_listening[ address ] = _socket
        self._sockets_listening[ _socket.fileno() ] = _socket
        ## __do_listen()


    def __onConnectionReady(self, fd, events):
        if fd not in self._sockets_listening :
            return

        _socket = self._sockets_listening[ fd ]
        while True:
            try:
                connection, address = _socket.accept()
                connection.setblocking(0)
            except socket.error, e:
                if e[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
                    return
                raise

            self._logger.info('%s (fd:%d) is connected', address, connection.fileno())
            #stream = iostream.IOStream(connection, io_loop=self._io_loop)
            #TcpConnection(stream, address, self._request_callback)
            session = Session(address, io_loop=self._io_loop, sock=connection)
            session.setAutoConnect( False )
            session.setProtocol( self._protocol_factory.getInstance() )
            session.setCodecChain( self.getCodecChain() )
            session.recv(self.__onRequestGot)
        ## __onConnectionReady()


    def __onRequestGot(self, session, state_code, data_or_error, request=None) :
        if protocol.PC_OK != state_code :
            self._logger.error('ignore bad request : [%d] %s', state_code, data_or_error)
            return

        data = data_or_error
        response,session_to = self._handler.handle( data, session )
        if response :
            if session_to == session or session_to is None :
                session.send( response, self.__onResponseSent )
            else :
                raise NotImplementedError,'How to handle session "%s"' % str(session_to)
        else :
            session.recv(self.__onRequestGot)
        ## __onRequest()

    def __onResponseSent(self, session, state_code, data_or_error, request=None) :
        # if state_code is not OK, just cancel the session
        session.recv(self.__onRequestGot)
        ## __onResponseSent()


    def serve_forever(self) :
        if not self._protocol_factory :
            raise RuntimeError, 'No ProtocolFactory is set for Server'
        if not self._handler :
            raise RuntimeError, 'No handler is set for Server'

        self._io_loop.start()
        ## serve_forever()

    ## class Server

